/* Copyright (C) 2014 InfiniDB, Inc.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; version 2 of
   the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   MA 02110-1301, USA. */

/******************************************************************************
 * $Id$
 *
 *****************************************************************************/
// WWW - Check on header.

/** @file
 * class ReplayTxnLog interface
 */

#include <string>
#include <list>
#include <ctime>

#include "calpontsystemcatalog.h"

/** @brief Replays remaining transactions after a backup is restored.
 *
 *
 * This class replays transactions after a full backup has been restored.  The class re-issues statements
 * from the DML/DDL log files allowing the database to be a state from a point in time after the backup
 * occurred.
 *
 * The user provides an Oracle user id and password as well as a stop date and time.  Transactions that were
 * committed after the time of the backup through before the stop date and time will be replayed in the order
 * that they were committed.
 *
 * This program only works under the following conditions:
 * 1) A backup was restored including the Calpont database files, the file containing the last txnId, and the
 *    corresponding Oracle files.
 * 2) The DML/DDL log files include the log entry for the backup and all of the transactions from the backup
 * time through the stop date and time provided by the user. 3) All of the transaction ids being replayed have
 * higher numbers than the transaction id from the backup. The procedure for resetting the txnId had not been
 * defined when this utility was written.  We need to take this utility into account when defining the
 * procedure.
 *
 */

class ReplayTxnLog
{
 public:
  /** @brief Constructor
   *  user -     		The database user id.  User will be prompted if passed blank.  Program will
   *			abort if invalid user / password combination.
   *
   *  password - 		The database user's password.  User will be prompted if passed blank.
   *
   *  stopDate - 		The stopdate formatted as "mm/dd/yy@hh:mm:ss" or "Now".  User will be
   *			prompted if passed blank or the format is incorrect.
   *
   *  ignoreBulkLoad -	True if bulk load log entries are to be ignored.  If false, the user
   *                      will be prompted with each bulk load entry.  Bulk loads are not included
   *			in the log, so the user would have to know that the bulk load will not
   *			cause any sql errors because of dependent data if bulk load entries
   *			are skipped.  Otherwise, the user would have to re-apply bulk loads
   *			each time this utility pauses.
   */
  ReplayTxnLog(const std::string& user, const std::string& password, const std::string& stopDate,
               const bool& ignoreBulkLoad, const bool& reportMode);

  /** @brief processes the transaction replay.
   */
  void process();

 private:
  /* Passed in constructor. */
  std::string fUser;
  std::string fPassword;
  std::string fStopDateStr;
  bool fIgnoreBulkLoad;

  /* Other private members. */
  execplan::CalpontSystemCatalog::SCN fStartVersionID;
  execplan::CalpontSystemCatalog::SCN fDDLCount;
  execplan::CalpontSystemCatalog::SCN fCommitCount;
  execplan::CalpontSystemCatalog::SCN fRollbackCount;
  time_t fProgramStarted;
  time_t fStopDate;
  std::string fCurrentYearStr;
  std::string fLastSchemaOwner;
  enum LogEntryType
  {
    DML,
    DDL,
    ROLLBACK,
    XCOMMIT,
    BULKLOAD,
    BACKUP_START,
    BACKUP_END,
    REPLAY_TXN_START,
    REPLAY_TXN_END
  };  // WWW - COMMIT reserved?

  struct LogEntry
  {
    execplan::CalpontSystemCatalog::SCN fVersionID;
    std::string fSqlStatement;
    time_t fLogTime;
    std::string fSchemaOwner;
    LogEntryType type;
    LogEntry() : fVersionID(0), fSqlStatement(""), fLogTime(0), fSchemaOwner("")
    {
    }
  };
  LogEntry fLogEntryFirstCommit;
  LogEntry fLogEntryLastCommit;
  struct ActiveTxn
  {
    execplan::CalpontSystemCatalog::SCN fVersionID;
    std::vector<LogEntry> fStatements;
  };
  std::vector<ActiveTxn> fActiveTxns;

  /* Private functions */
  void validateInput();
  std::list<std::string> getLogFileNames();
  void replayTransactions(std::list<std::string>& files);
  void processStatement(const LogEntry& logEntry);
  int split(vector<std::string>& v, const std::string& str, const char& c);
  bool getNextCalpontLogEntry(ifstream& dataFile, LogEntry& logEntry);
  bool displaySummaryAndConfirm(const LogEntry& logEntry);
  void displayProgress(const std::string& fileName);
  void displayFinalSummary();
  time_t convertLogDateStr(const std::string& mth, const std::string& day, const std::string& time);
  void checkDefinitions();

  ReplayTxnLog(const ReplayTxnLog& rhs);             // no copies
  ReplayTxnLog& operator=(const ReplayTxnLog& rhs);  // no assignments
};

namespace replaytxnlog
{
/** @brief wraps an Oracle session
 * This class provides an Oracle session interface allowing a session to be started, DML statements
 * to be executed, and closing the session.
 */
class DBSession
{
 public:
  /** @brief starts the session.
   */
  void startSession(const std::string& connectAsUser);

  /** @brief issues a DML or DDL statement.
   */
  void issueStatement(const std::string& stmt);

  /** @brief issues a commit.
   */
  void commit();

  /** @brief issues a rollback.
   */
  void rollback();

  /** @brief ends the session.
   */
  void endSession();

  /** @brief returns the connected user or empty string if a session is not active.
   */
  std::string getConnectedUser();

  /** @brief constructor
   */
  DBSession();

 private:
  DBSession(const DBSession& rhs);  // private ctor
  DBSession& operator=(const DBSession& rhs);
  std::string fConnectedUser;
};

}  // namespace replaytxnlog
